package com.tgit.sso.core.conf;

import com.tgit.sso.core.util.JacksonUtil;
import com.tgit.sso.core.util.JedisUtil;
import com.tgit.sso.core.util.SsoLoginHandlerUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 基于web-sso登录过滤器-使用于客户端
 * 
 * @author 范再军 2018年9月12日
 */
public class SsoFilter extends HttpServlet implements Filter {

	private static final long serialVersionUID = -627293949552741386L;

	private static Logger LOGGER = LoggerFactory.getLogger(SsoFilter.class);
	//sso-server域名地址-跳转地址
	private String ssoServer;
	//sso-server判断通断地址-私有云部署内网配置地址 
	private String ssoConnectUrl;
	//sso-客户端登出地址
	private String logoutPath;
	//sso-登录成功客户端跳转地址
	private String clientServer;
	//redis地址
	private String redisAddress;
	//redis密码
	private String redisPassword;
	 //是否走单点登录地址 ssoFlag="sso"走单点登录，不配置为null就是不走sso
	private String ssoFlag;

	private List<String> excludeUrl = new ArrayList<String>();

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		/**
		 * 初始化,工程启动会被执行
		 */
		ssoServer = filterConfig.getInitParameter(SsoConf.SSO_SERVER);
		if (ssoServer != null && ssoServer.trim().length() > 0) {
			logoutPath = filterConfig.getInitParameter(SsoConf.SSO_LOGOUT_PATH);
			clientServer = filterConfig.getInitParameter(SsoConf.CLIENT_SERVER);
			redisAddress = filterConfig.getInitParameter(SsoConf.REDIS_ADDRESS);
			redisPassword = filterConfig.getInitParameter(SsoConf.REDIS_PASSWORD);
			ssoConnectUrl = filterConfig.getInitParameter(SsoConf.SSO_CONNECT_URL);
			ssoFlag = filterConfig.getInitParameter(SsoConf.SSO_FLAG);
			//如果配置了redis就加入
			if(!StringUtils.isBlank(this.redisAddress)) {
	    		if(StringUtils.isBlank(this.redisPassword)) {
	    			JedisUtil.init(redisAddress);
	    			LOGGER.info("REDIS初始化非密码模式配置");
	    		}else {
	    			JedisUtil.init(redisAddress, redisPassword);    
	    			LOGGER.info("REDIS初始化密码模式配置");
	    		}
	    	}else {
	    		LOGGER.error("REDIS没有配置");
	    	}
		}

		String exactMatchedURLString = filterConfig.getInitParameter("excludedURLs");
		this.setExcludeUrl(exactMatchedURLString);

		LOGGER.info("SsoFilter init" + ssoServer + ":" + ssoServer);
	}

	/**
	 * 提供给实现类调用
	 * @param ssoServer
	 * @param ssoConnectUrl
	 * @param logoutPath
	 * @param clientServer
	 * @param redisAddress
	 * @param redisPassword
	 * @param ssoFlag
	 * @param excludedURLs
	 */
	public void init(String ssoServer,String ssoConnectUrl,String logoutPath,String clientServer,String redisAddress,String redisPassword,String ssoFlag, String excludedURLs) {
		this.init(ssoServer, ssoConnectUrl, logoutPath, clientServer, redisAddress, redisPassword, ssoFlag);
		this.setExcludeUrl(excludedURLs);
	}

	/**
	 * 提供给实现类调用
	 * @param ssoServer
	 * @param ssoConnectUrl
	 * @param logoutPath
	 * @param clientServer
	 * @param redisAddress
	 * @param ssoFlag
	 */
	public void init(String ssoServer,String ssoConnectUrl,String logoutPath,String clientServer,String redisAddress,String redisPassword,String ssoFlag) {
		this.ssoServer=ssoServer;
		this.ssoConnectUrl=ssoConnectUrl;
		this.logoutPath=logoutPath;
		this.redisAddress=redisAddress;
		this.redisPassword=redisPassword;
		this.clientServer=clientServer;
		this.ssoFlag=ssoFlag;
		//如果配置了redis就加入
		if(!StringUtils.isBlank(this.redisAddress)) {
    		if(StringUtils.isBlank(this.redisPassword)) {
    			JedisUtil.init(redisAddress);
    			LOGGER.info("REDIS初始化非密码模式配置");
    		}else {
    			JedisUtil.init(redisAddress, redisPassword);    
    			LOGGER.info("REDIS初始化密码模式配置");
    		}
    	}else {
    		LOGGER.error("REDIS没有配置");
    	}
		LOGGER.info("SsoFilter init" + ssoServer + ":" + ssoServer);
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		if(ssoFlag !=null && !ssoFlag.isEmpty() && "sso".equals(ssoFlag)) {
			HttpServletRequest req = (HttpServletRequest) request;
			HttpServletResponse res = (HttpServletResponse) response;

			String url = req.getRequestURI();
			for (int i = 0; i < this.excludeUrl.size(); i++) {
				if(url.contains(this.excludeUrl.get(i))) {
					chain.doFilter(request, response);
					return;
				}
			}

			String servletPath = ((HttpServletRequest) request).getServletPath();
			String link = req.getRequestURL().toString();
	
			// 登出过滤处理
			if (logoutPath != null && logoutPath.trim().length() > 0 && logoutPath.equals(servletPath)) {
				// 清除cookie
				SsoLoginHandlerUtils.removeSessionIdInCookie(req, res);
				// 重定向到sso服务端登出地址
				String logoutPageUrl = ssoServer.concat(SsoConf.SSO_LOGOUT) + "?" + SsoConf.REDIRECT_URL + "="
						+ getRedirectUrl(req);
				res.sendRedirect(logoutPageUrl);
				return;
			}
	
			// 登录过滤处理
			SsoUser ssoUser = null;
	
			// req中得到cookie信息，从redis里获取数据cookie信息-对用用户信息
			String cookieSessionId = SsoLoginHandlerUtils.getSessionIdByCookie(req);
			ssoUser = SsoLoginHandlerUtils.loginCheck(cookieSessionId);
			// 判断路径参数是否有cookie，有的话，将路径参数的cookie存入到redis中，并且设置到响应体中
			String paramSessionId = request.getParameter(SsoConf.SSO_SESSIONID);
			if (paramSessionId != null) {
				ssoUser = SsoLoginHandlerUtils.loginCheck(paramSessionId);
				if (ssoUser != null) {
					SsoLoginHandlerUtils.setSessionIdInCookie(res, paramSessionId);
					// 首次登录  获取语言
					String ssoLocale = SsoLoginHandlerUtils.localeCheck(paramSessionId);
					// 首次登录
					res.sendRedirect(getRedirectUrl(req) + "?locale=" + ssoLocale);
					return;
				} else {
					// 不存在情况-清除cookie
					SsoLoginHandlerUtils.removeSessionIdInCookie(req, res);
				}
			}
	
			// 如果路径参数和req的cookie中都没有从redis拿到想要的值。就说明用户没有登录
			if (ssoUser == null) {
				// 判断报文格式
				String header = req.getHeader("content-type");
				boolean isJson = header != null && header.contains("json");
				if (isJson) {
					// json格式信息
					res.setContentType("application/json;charset=utf-8");
					res.getWriter().println(JacksonUtil.writeValueAsString(SsoConf.SSO_LOGIN_FAIL_RESULT));
					return;
				} else {
					// 重定向到登录
					String loginPageUrl = ssoServer.concat(SsoConf.SSO_LOGIN) + "?" + SsoConf.REDIRECT_URL + "=" + link;
					res.sendRedirect(loginPageUrl);
					return;
				}
	
			}
			// 判断服务器联通状态
			if (!SsoConf.isReachableUrl(this.ssoConnectUrl)) {
				SsoLoginHandlerUtils.removeSessionIdInCookie(req, res);
				String logoutPageUrl = ssoServer.concat(SsoConf.SSO_LOGOUT) + "?" + SsoConf.REDIRECT_URL + "="
						+ getRedirectUrl(req);
				res.sendRedirect(logoutPageUrl);
				return;
			}
			// 嵌入sso用户信息
			request.setAttribute(SsoConf.SSO_USER, ssoUser);

			//如果是imax，更新sessionid 和 redis 时间
			String imaxUrl = SsoConfigurationStore.get(SsoConf.IMAX_URL);
			if(imaxUrl != null && imaxUrl.length() > 0) {
				String[] imaxUrlArr = imaxUrl.split(SsoConf.COMMA_SEPARATOR);
				for (String item : imaxUrlArr) {
					if(getRedirectUrl(req).contains(item)) {
						//再次执行插入到redis
						String ssoLocale = SsoLoginHandlerUtils.localeCheck(cookieSessionId);
						SsoLoginHandlerUtils.login(res, cookieSessionId, ssoUser, ssoLocale);
						break;
					}
				}
			}
		}
		// 不管是否走sso，（已经登录，继续操作）
		chain.doFilter(request, response);
		return;
	}

	/**
	 * 设置excludeUrl
	 * @param excludedURLs
	 */
	private void setExcludeUrl(String excludedURLs) {
		if(excludedURLs != null) {
			String[] tmps = excludedURLs.split(",");
			for (String tmp : tmps) {
				tmp = tmp.trim();
				if (tmp.length() > 0) {
					this.excludeUrl.add(tmp);
				}
			}
		}
	}

	/**
	 * 获取重定向url, 加上上下文
	 * @param req
	 * @return
	 */
	private String getRedirectUrl(HttpServletRequest req) {
		String contextPath = req.getContextPath();
		if(!this.clientServer.contains(contextPath)) {
			return this.clientServer + contextPath;
		}

		return this.clientServer;
	}
}
